package withouther.system.plugins.system.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;
import withouther.system.core.base.BaseServiceImpl;
import withouther.system.core.security.entity.UserInfo;
import withouther.system.core.security.jwt.JwtUtil;
import withouther.system.core.util.RedisCacheManager;
import withouther.system.core.util.page.PageQuery;
import withouther.system.core.util.page.PageUtils;
import withouther.system.plugins.system.entity.AuthUser;
import withouther.system.plugins.system.entity.Role;
import withouther.system.plugins.system.entity.User;
import withouther.system.plugins.system.entity.UserRole;
import withouther.system.plugins.system.mapper.UserMapper;
import withouther.system.plugins.system.mapper.UserRoleMapper;
import withouther.system.plugins.system.service.MenuService;
import withouther.system.plugins.system.service.RoleService;
import withouther.system.plugins.system.service.UserService;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service("useService")
@CacheConfig(cacheNames = "user")
public class UseServiceImpl extends BaseServiceImpl<UserMapper, User> implements UserService {
	@Resource
	private UserMapper userMapper;

	@Resource
	private UserRoleMapper userRoleMapper;

	@Resource
	private AuthenticationManager authenticationManager;
	@Resource
	private MenuService menuService;

	@Resource
	private RoleService roleService;

	@Resource
	private RedisCacheManager redisCacheManager;

	@Override
//	@Cacheable
	public PageUtils queryPage(Map<String, Object> params) {
		String userName = Convert.toStr(params.get("userName"), "").trim();
		if (StrUtil.isNotEmpty(userName)) {
			params.put("userName", "%" + userName + "%");
		}
		Page<User> page = new PageQuery<User>().getPageOr(params);
		// 不查询总记录数
		page.setSearchCount(false);
		page.setTotal(this.userMapper.countUser(params));
		return new PageUtils(this.userMapper.selectUserListByParam(page, params));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	@CacheEvict(allEntries = true)
	public boolean addUser(User user) {
//		String salt = String.valueOf(System.currentTimeMillis());
		user.setPassword(new MD5().digestHex(user.getPassword()));
		Integer result = baseMapper.insert(user);
		if (result < 1) {
			return false;
		}
		Set<Role> roles = user.getRoleList();
		addUserRole(roles, user.getId());
		return true;
	}

	private void addUserRole(Set<Role> roles, Long userId) {
		Set<UserRole> userRoles = new HashSet<>();
		for (Role role : roles) {
			UserRole userRole = new UserRole();
			userRole.setUid(userId);
			userRole.setRid(role.getId());
			userRoles.add(userRole);
		}
		if (!userRoles.isEmpty()) {
			userRoleMapper.inserts(userRoles);
		}
	}
	@Override
	@Transactional(rollbackFor = Exception.class)
	@CacheEvict(allEntries = true)
	public boolean updateUser(User user) {
		user.setPassword(new MD5().digestHex(user.getPassword()));
		Integer result = baseMapper.updateById(user);
		if (result < 1) {
			return false;
		}
		deleteUserRole(user.getId());
		addUserRole(user.getRoleList(), user.getId());
		return true;
	}

	private void deleteUserRole(Long uid) {
		UpdateWrapper<UserRole> wrapper = new UpdateWrapper<>();
		wrapper.eq("uid", uid);
		userRoleMapper.delete(wrapper);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	@CacheEvict(allEntries = true)
	public boolean deleteUserByIdAndRole(List<Long> ids) {
		if (ids.isEmpty()) {
			return false;
		}
		for (Long id : ids) {
			this.removeById(id);
			deleteUserRole(id);
		}
		return true;
	}

	@Override
	public Map<String, Object> login(AuthUser authUser) {
		String code = (String) redisCacheManager.get(authUser.getUuid());
		if (StringUtils.isEmpty(code)) {
			throw new RuntimeException("验证码输入错误，请重新输入");
		}
		if (!code.equals(authUser.getCode())) {
			throw new RuntimeException("验证码输入错误，请重新输入");
		}
		authUser.setPassword(new MD5().digestHex(authUser.getPassword()));
		Map<String, Object> resultMap = new HashMap<>();
		//用户验证
		Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authUser.getUsername(), authUser.getPassword()));
		//存储认证信息
		SecurityContextHolder.getContext().setAuthentication(authentication);
		//生成token
		UserInfo userDetail = (UserInfo) authentication.getPrincipal();
		resultMap.put("token",  JwtUtil.generateToken(userDetail));
		resultMap.put("user",  userDetail);
		return resultMap;
	}

	@Override
	public Set<String> findPermsByUserId(Long userId) {
		return menuService
				.findPermsByUserId(userId)
				.stream()
				.map(menu -> menu.getPermission())
				.filter(StringUtils::isNotEmpty)
				.collect(Collectors.toSet());
	}

	@Override
	public Set<String> findRoleIdByUserId(Long userId) {
		return roleService
				.selectUserRoleListByUserId(userId)
				.stream()
				.map(role -> "ROLE_" + role.getId())
				.collect(Collectors.toSet());
	}

	@Override
	public UserInfo getUserInfo() {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication == null) {
			return null;
		}
		if (!(authentication.getPrincipal() instanceof UserInfo)) {
			return null;
		}
		UserInfo userInfo = (UserInfo)authentication.getPrincipal();
		log.info(userInfo.toString());
		return userInfo;
	}


}